package tech.horgee.service;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.horgee.common.MyCode;
import tech.horgee.entity.Role;
import tech.horgee.entity.RoleMenu;
import tech.horgee.entity.UserRole;
import tech.horgee.mapper.RoleMapper;
import tech.horgee.mapper.RoleMenuMapper;
import tech.horgee.mapper.UserRoleMapper;
import tech.horgee.utils.AssertUtil;
import tech.horgee.utils.CacheUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * 角色信息 服务类
 *
 * @author haojun
 * @since 2022-06-12
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class RoleService {
    private final RoleMapper mapper;
    private final RoleMenuMapper roleMenuMapper;
    private final UserRoleMapper userRoleMapper;

    private final String CACHE_ROLE_MENUS = "roleMenus";

    /**
     * 分页查询列表
     *
     * @param page   分页对象
     * @param entity 查询实体
     * @return 所有数据
     */
    public Page<Role> page(Page<Role> page, Role entity) {
        return mapper.selectPage(page, entity.wrapper());
    }

    /**
     * 不分页查询列表
     *
     * @param entity 实体对象
     * @return 查询结果
     */
    public List<Role> list(Role entity) {
        LambdaQueryWrapper<Role> wrapper = entity.wrapper();
        wrapper.select(Role::getRoleId, Role::getRoleName);
        return mapper.selectList(wrapper);
    }

    /**
     * 通过主键查询单条数据
     *
     * @param id 主键
     * @return 单条数据
     */
    public Role getById(Long id) {
        return mapper.selectById(id);
    }

    /**
     * 新增单条数据
     *
     * @param entity 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void save(Role entity) {
        checkRoleNameUnique(entity);
        mapper.insert(entity);
        insertRoleMenu(entity);
    }

    /**
     * 修改单条数据
     *
     * @param entity 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void update(Role entity) {
        checkRoleNameUnique(entity);
        // 删除角色与菜单关联
        roleMenuMapper.deleteRoleMenuByRoleId(entity.getRoleId());
        mapper.updateById(entity);
        insertRoleMenu(entity);
    }

    /**
     * 批量删除数据
     *
     * @param ids 主键集合
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteByIds(Set<Long> ids) {
        boolean exists = userRoleMapper.exists(new LambdaQueryWrapper<UserRole>().in(UserRole::getRoleId, ids));
        if (exists) throw MyCode.DATA_VERIFY_ERROR.toException("角色已绑定用户，无法删除");
        roleMenuMapper.deleteRoleMenu(ids);
        AssertUtil.updated(mapper.deleteBatchIds(ids));
        ids.forEach(id -> CacheUtils.evict(CACHE_ROLE_MENUS, id));
    }

    /**
     * 新增角色菜单信息
     *
     * @param role 角色对象
     */
    public void insertRoleMenu(Role role) {
        CacheUtils.evict(CACHE_ROLE_MENUS, role.getRoleId());
        // 新增用户与角色管理
        if (CollUtil.isNotEmpty(role.getMenuIds())) {
            List<RoleMenu> list = new ArrayList<>();
            for (Long menuId : role.getMenuIds()) {
                RoleMenu rm = new RoleMenu();
                rm.setRoleId(role.getRoleId());
                rm.setMenuId(menuId);
                list.add(rm);
            }
            if (!list.isEmpty()) roleMenuMapper.batchRoleMenu(list);
        }
    }

    /**
     * 校验角色名称是否唯一
     *
     * @param role 角色信息
     */
    public void checkRoleNameUnique(Role role) {
        Long roleId = Objects.isNull(role.getRoleId()) ? Long.valueOf(-1L) : role.getRoleId();
        Role info = mapper.checkRoleNameUnique(role.getRoleName());
        if (Objects.nonNull(info) && info.getRoleId().longValue() != roleId.longValue()) {
            throw MyCode.DUPLICATE_KEY.toException("角色名称已存在");
        }
    }

    @Cacheable(cacheNames = CACHE_ROLE_MENUS, key = "#roleId")
    public Set<Long> selectMenuIdsByRoleId(Long roleId) {
        return roleMenuMapper.selectMenuIdsByRoleId(roleId);
    }

}
